﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;

namespace Mindfor.Web.Data
{
	/// <summary>
	/// Class for user data object.
	/// </summary>
	public class User : IdEntity
	{
		static CmsRoles[] s_baseRoles;

		/// <summary>
		/// Returns base roles array.
		/// </summary>
		protected static CmsRoles[] GetBaseRoles()
		{
			// initialize
			if (s_baseRoles == null)
			{
				string[] names = Enum.GetNames(typeof(CmsRoles));
				s_baseRoles = new CmsRoles[names.Length];
				for (int i = 0; i < names.Length; i++)
					s_baseRoles[i] = (CmsRoles)Enum.Parse(typeof(CmsRoles), names[i]);
			}

			return s_baseRoles;
		}

		/// <summary>
		/// Gets or sets login.
		/// </summary>
		public virtual string Login { get; set; }

		/// <summary>
		/// Gets or sets hashed password.
		/// </summary>
		protected virtual string Password { get; set; }

		/// <summary>
		/// Gets or sets e-mail.
		/// </summary>
		public virtual string Email { get; set; }

		/// <summary>
		/// Gets or sets whether user is approved by the system.
		/// </summary>
		public virtual bool IsApproved { get; set; }

		/// <summary>
		/// Gets or sets whether user is blocked and can not login.
		/// </summary>
		public virtual bool IsBanned { get; set; }

		/// <summary>
		/// Gets or sets registration date.
		/// </summary>
		public virtual DateTime RegDate { get; set; }

		/// <summary>
		/// Gets or sets last activity date.
		/// </summary>
		public virtual DateTime LastActivityDate { get; set; }

		/// <summary>
		/// Gets or sets last password change date.
		/// </summary>
		public virtual DateTime LastPasswordChangeDate { get; set; }

		/// <summary>
		/// Gets or sets guid used to activate user.
		/// </summary>
		public virtual Guid? ActivationGuid { get; set; }

		/// <summary>
		/// Gets or sets guid for password recovery.
		/// </summary>
		public virtual Guid? PasswordRecoveryGuid { get; set; }

		/// <summary>
		/// Gets or sets user roles.
		/// </summary>
		public virtual IList<Role> Roles { get; set; }

		public override string ToString()
		{
			return Login;
		}

		/// <summary>
		/// Validates that user has any of roles.
		/// </summary>
		/// <param name="roles">Roles to check.</param>
		/// <returns><c>True</c>, is user in role. Otherwise <c>False</c>.</returns>
		public virtual bool IsInRole(params object[] roles)
		{
			foreach (object role in roles)
			{
				if (this.Roles.Any(r => role.ToString().ToLower() == r.Name.ToLower()))
					return true;
			}
			return false;
		}

		/// <summary>
		/// Returns true, if user has any of base roles.
		/// </summary>
		/// <returns>True, if user has any of base roles; otherwise - false.</returns>
		public virtual bool HasAnyBaseRole()
		{
			return IsInRole(GetBaseRoles());
		}

		/// <summary>
		/// Validate user password.
		/// </summary>
		/// <param name="password">Password to match.</param>
		/// <returns>True, is password matches. Otherwise, false.</returns>
		public virtual bool ValidatePassword(string password)
		{
			// validate AD
			//if (!String.IsNullOrEmpty(Domain))
			//{
			//    try
			//    {
			//        string ldap = DirectoryServiceHelper.DomainToLdap(Domain);
			//        DirectoryEntry entry = new DirectoryEntry(ldap, Login, password);
			//        object obj = entry.NativeObject; // bind to the native AdsObject to force authentication.

			//        DirectorySearcher s = new DirectorySearcher(entry);
			//        s.Filter = "(SAMAccountName=" + Login + ")";
			//        s.PropertiesToLoad.Add("cn");

			//        SearchResult res = s.FindOne();
			//        return res != null;
			//    }
			//    catch
			//    {
			//        return false;
			//    }
			//}

			// validate local user
			if (password == null)
				return this.Password == null;
			return this.Password.Equals(HashMD5(password), StringComparison.Ordinal);
		}

		/// <summary>
		/// Changes user password.
		/// </summary>
		/// <param name="newPassword">New user password.</param>
		public virtual void ChangePassword(string newPassword)
		{
			//if (!String.IsNullOrEmpty(this.Domain))
			//	throw new InvalidOperationException("Cannot change password of directory service user.");

			if (newPassword == null)
				this.Password = null;
			else
				this.Password = HashMD5(newPassword);
			this.LastPasswordChangeDate = DateTime.UtcNow;
		}

		/// <summary>
		/// Generates new password and sets it for this user.
		/// </summary>
		/// <returns>New user password.</returns>
		public virtual string GeneratePassword()
		{
			// generate pwd
			string pwd = "";
			Random r = new Random();
			int a = (int)'a';
			int z = (int)'z';
			int n0 = (int)'0';
			int n9 = (int)'9';

			while (pwd.Length < 8)
			{
				switch (r.Next(0, 2))
				{
					case 0:
						pwd += (char)r.Next(a, z + 1);
						break;
					case 1:
						pwd += (char)r.Next(n0, n9 + 1);
						break;
				}
			}

			// change pwd
			ChangePassword(pwd);
			return pwd;
		}

		/// <summary>
		/// Returns MD5 hash from input string using unicode encoding.
		/// </summary>
		/// <param name="input">Input string.</param>
		/// <returns>MD5 hash.</returns>
		private string HashMD5(string input)
		{
			// Получить байты
			System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding();
			byte[] bytes = encoding.GetBytes(input);

			// Получить хэш
			using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
			{
				bytes = md5.ComputeHash(bytes);
			}

			// Получить строку
			return BitConverter.ToString(bytes).ToLower();
		}
	}
}
